package com.barudisshu.service;

import com.barudisshu.domain.Storage;
import com.barudisshu.repository.StorageRepository;
import com.barudisshu.utils.AsyncScalr;
import com.barudisshu.utils.Scalr;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

@Service
public class StorageServiceImpl implements StorageService {

    //Save the uploaded file to this folder
    private static final String UPLOADED_FOLDER = "/opt/storage/";
    private static final String SERVER_ADDRESS = System.getenv("UPLOAD_HOST");
    private static final String SERVER_PORT = System.getenv("UPLOAD_PORT");

    private final StorageRepository storageRepository;

    @Autowired
    public StorageServiceImpl(StorageRepository storageRepository) {
        this.storageRepository = storageRepository;
    }

    //save file
    public List<String> saveUploadedFiles(List<MultipartFile> files) throws IOException {

        List<String> filenames = new ArrayList<>();
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                continue; //next pls
            }


            byte[] bytes = file.getBytes();

            // check file exsits or not
            String hash = hash(bytes);

            Storage storage = storageRepository.findByHashed(hash);

            if(storage != null){
                filenames.add(storage.getPath());
            }else {
                // save to database

                String filename = RandomStringUtils.randomAlphabetic(8) + getExtension(file.getOriginalFilename());
                Path path = Paths.get(UPLOADED_FOLDER + filename);

                String domainPath = "http://" + SERVER_ADDRESS + ":" + SERVER_PORT +"/file/" + filename;

                Storage save = new Storage();
                save.setAlpha(filename);
                save.setHashed(hash);
                save.setOrigin(file.getOriginalFilename());
                save.setPath(domainPath);
                save.setAbsolutePath(path.toString());
                save.setSize(file.getSize());
                save.setContentType(file.getContentType());

                storageRepository.save(save);

                filenames.add(domainPath);

                Files.write(path, bytes);

            }
        }
        return filenames;
    }

    public ResponseEntity<?> download(String filename, Integer width, Integer height) throws Exception {
        Storage storage = findByFilename(filename);
        File file = new File(storage.getAbsolutePath());

        if (storage.getContentType().startsWith("image/") && width != null && height != null) {
            // It's an image.
            BufferedImage bi = ImageIO.read(file);
            BufferedImage scalaBi = AsyncScalr.resize(bi, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_EXACT, width, height).get();

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(scalaBi, FilenameUtils.getExtension(storage.getOrigin()), baos );
            baos.flush();
            byte[] imageInByte = baos.toByteArray();
            baos.close();

            ByteArrayResource resource = new ByteArrayResource(imageInByte);

            return ResponseEntity.ok()
                    .contentLength(imageInByte.length)
                    .contentType(MediaType.parseMediaType(storage.getContentType()))
                    .body(resource);
        }else {

            Path path = Paths.get(storage.getAbsolutePath());
            ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));

            return ResponseEntity.ok()
                    .contentLength(storage.getSize())
                    .contentType(MediaType.parseMediaType(storage.getContentType()))
                    .body(resource);
        }
    }



    @Override
    public Storage findByFilename(String filename) {
        return storageRepository.findByAlpha(filename);
    }

    private static String hash(byte[] bytes) throws IOException {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        assert md5 != null;
        byte[] digest = md5.digest(bytes);
        return new BigInteger(1, digest).toString(16);
    }

    private static String getExtension(String originFilename) {
        String extension = FilenameUtils.getExtension(originFilename);
        if (extension != null) {
            extension = "." + extension;
        }
        return extension;
    }
}
